package com.mvc.ext;

import com.mvc.annotation.ExtController;
import com.mvc.annotation.ExtRequestMapping;
import com.mvc.utils.ClassUtil;
import org.apache.commons.lang3.StringUtils;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author xinwang
 * @date 2020/3/1 15:05
 **/
public class ExtDispatcherServlet extends HttpServlet {
    //bean容器    key：类名id   value：对象
    private ConcurrentHashMap<String,Object> springMvcBeans=new ConcurrentHashMap<>();
    //key: 请求地址  value: 类
    private ConcurrentHashMap<String,Object> urlBeans=new ConcurrentHashMap<>();
    //key：请求地址  value:方法名
    private ConcurrentHashMap<String,String> urlMethods=new ConcurrentHashMap<>();
    @Override
    public void init() throws ServletException {
        //1.获取当前包下所有的类
        List<Class<?>> classes = ClassUtil.getClasses("com.mvc.controller");
        try {
            //2.将带有注解的类 注入容器中
            findClassMVCAnnotation(classes);
        }catch (Exception e){

        }
        //3.将映射url和方法关联
        extHandlerMapping();
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req,resp); }
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //获取请求url地址
        String requestURI = req.getRequestURI();
        if (StringUtils.isEmpty(requestURI)){
            return;
        }
        //从map集合中获取控制对象
        Object obj = urlBeans.get(requestURI);
        if (obj==null){
            resp.getWriter().println("not found 404 url");
            return;
        }
        //从url地址获取对应的映射方法
        String methodName = urlMethods.get(requestURI);
        if (StringUtils.isEmpty(methodName)){
            resp.getWriter().println("not found method");
            return;
        }
        //使用java反射机制获取方法的返回结果
        String result = (String) methodInvoke(obj, methodName);
        //resp.getWriter().println(result);
        //调用视图解析器渲染给页面展示
        extResourceViewResolver(result,req,resp);
    }

    /**
     * 使用java反射机制调用方法
     * @param object   对象
     * @param methodName   方法名
     * @return
     */
    public Object methodInvoke(Object object,String methodName){
        try {
            Method method = object.getClass().getMethod(methodName);
            Object result = method.invoke(object);
            return result;
        }catch (Exception e){
            return null;
        }
    }


    /**
     * 将扫包 扫到带有注解的类 注入到容器中，存放Map集合  Key为默认类名小写  value为 当前类名对象
     * @param classes
     * @throws InstantiationException
     * @throws IllegalAccessException
     * @throws ClassNotFoundException
     */
    public void  findClassMVCAnnotation(List<Class<?>> classes) throws InstantiationException, IllegalAccessException, ClassNotFoundException {
        for (Class<?> classInfo : classes) {
            ExtController extController = classInfo.getAnnotation(ExtController.class);
            //判断当前类有没有加注解(@ExtController)
            if (extController !=null){
                //获取类名 首字母小写
                String beanId = ClassUtil.toLowerCaseFirstOne(classInfo.getName());
                //用当前class创建对象
                Object obj = ClassUtil.newInstance(classInfo);
                //将对象 存入容器中
                springMvcBeans.put(beanId,obj);
            }
        }
    }


    /**
     * 将url与方法进行映射
     */
    public void  extHandlerMapping(){
        //1.遍历springMvcBeans 容器 判断类是否有url映射注解(@ExtRequestMapping)
        for (Map.Entry<String, Object> entry : springMvcBeans.entrySet()) {
            //获取bean 对象
            Object bean = entry.getValue();
            //拿到当前对象 class
            Class<?> beanClass = bean.getClass();
            //判断当前类上是否有加url映射注解(@ExtRequestMapping)
            ExtRequestMapping extClassRequestMapping = beanClass.getAnnotation(ExtRequestMapping.class);
            String baseUrl="";
            if (extClassRequestMapping !=null){
                //获取类上的url地址
                 baseUrl = extClassRequestMapping.value();
            }
            //获取当前类的所有方法
            Method[] methods = beanClass.getMethods();
            for (Method method : methods) {
                ExtRequestMapping extMethodRequestMapping = method.getAnnotation(ExtRequestMapping.class);
                //判断当前类的方法上是否有加url映射注解(@ExtRequestMapping)
                if (extMethodRequestMapping!=null){
                    baseUrl=baseUrl+extMethodRequestMapping.value();
                    urlBeans.put(baseUrl,bean);
                    urlMethods.put(baseUrl,method.getName());
                }
            }
        }
    }

    private void extResourceViewResolver(String pageName,HttpServletRequest rep,HttpServletResponse resp) throws ServletException, IOException {
        String prefix="/";
        String suffix=".jsp";
        rep.getRequestDispatcher(prefix+pageName+suffix).forward(rep,resp);
    }
}
